/*********************************************************
 Version 1.000
 
 Code provided by Michael Hemsley and Anthony Dick
 for the course 
 COMP SCI 3014/7090 Computer Graphics
 School of Computer Science
 University of Adelaide
 
 Permission is granted for anyone to copy, use, modify, or distribute this
 program and accompanying programs and documents for any purpose, provided
 this copyright notice is retained and prominently displayed, along with
 a note saying that the original programs are available from the aforementioned 
 course web page. 
 
 The programs and documents are distributed without any warranty, express or
 implied.  As the programs were written for research purposes only, they have
 not been tested to the degree that would be advisable in any important
 application.  All use of these programs is entirely at the user's own risk.
 *********************************************************/

/**
 * 	tgaimage.h
 * 	Loads tga image files and stores OpenGL specific formatting information.
 * 	Only handles a minimal set of different tga pixel formats.
 */

#ifndef TEXTURE_LOADER_H
#define TEXTURE_LOADER_H

#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string>

#include <GL/glew.h>
//#include "GLee.h"

#define BITS_PER_CHANNEL 8

class TgaImage {
public:
	int channels;		// Number of channels (I|RGB|RGBA)
	int dataType;		// openGL image data type
	int format;			// openGL image data format
	// Image attributes
	unsigned int imgWidth;
	unsigned int imgHeight;
	// Raw image data
	unsigned int imgDataLen;
	unsigned char *imgData;
	
    // empty constructor
    TgaImage(){};
	/**
	 * Load a .tga texture from a file.
	 */
	TgaImage(const char *filename) {
		// Check file extension
		if (strstr(filename, ".tga") == NULL) {
			std::cout << "Can't load " << filename << ". Only .tga files supported. Quitting\n";
			exit(1);
		}

		// Parse in file as TGA.
		parseTGA(filename);
	}
	/**
	 * Destructor
	 */
	~TgaImage() {
		delete [] imgData;
	}
    
    //load file
    void load(const char *filename) {
		// Check file extension
		if (strstr(filename, ".tga") == NULL) {
			std::cout << "Can't load " << filename << ". Only .tga files supported. Quitting\n";
			exit(1);
		}
        
		// Parse in file as TGA.
		parseTGA(filename);
	}

		
	/**
	 * Loads the given .tga file into imgData array and records attributes of the texture
	 * @param filename, the .tga file to load
	 */
	void parseTGA(const char *filename) {
		std::ifstream in;
		in.open (filename);
		if (!in.is_open()) {
			std::cout << "Error opening file " << filename << " in texture loader. Quitting\n";
			exit(1);
		}

		// Header
		char s[18];
		if (in.good())
			in.read(s, 18);

		unsigned char idLen = s[0];				// Extra information length
		unsigned char colMapType = s[1];		// If colour map is used (see below)
		unsigned char imgType = s[2] & 0x7;		// Type of image (see below)

		imgWidth = 	(unsigned short) (((unsigned char)s[13]<<8) | (unsigned char)s[12]);
		imgHeight = (unsigned short) (((unsigned char)s[15]<<8) | (unsigned char)s[14]);
		unsigned char imgDepth = s[16];

		// Only handle no colour map images
		if (colMapType != 0) {
			std::cout << "Error loading: " << filename << " colour maps not supported. Quitting\n";
			exit(1);
		}

		// Skip id field
		if (idLen != 0) {
			in.seekg(idLen, std::ios_base::cur);
			std::cout << "Skipped " << idLen << " bytes.\n";
		}
		
		if (imgType == 2) {		// Image is colour
			if (imgDepth == 24) {	// RGB
				channels = 3;
				dataType = GL_RGB8;
				format = GL_RGB;
			} else if (imgDepth == 32) {	// RGBA
				channels = 4;
				dataType = GL_RGBA8;
				format = GL_RGBA;
			} else {
				std::cout << "Error loading: " << filename << " number of channels  : " << ((unsigned int)imgDepth) << " not supported. Quitting\n";
				exit(1);
			}
		} else if (imgType == 3) {	// Image is greyscale
			channels = 1;
			dataType = GL_LUMINANCE8;
			format = GL_LUMINANCE;
		} else {
			std::cout << "Error loading: " << filename << " only uncompressed true-color/greyscale supported. Quitting\n";
			exit(1);
		}

		// Space for data
		imgDataLen = imgWidth*imgHeight*channels;
		imgData = new unsigned char[imgDataLen];

		char *col = new char[channels];
		if (imgDepth == 32) {	// Do alpha read
			// Read in channels of data at a time and store
				col = new char[channels];
				for (unsigned int i=0; i<imgDataLen; i+=channels) {
					if (in.good()) {
						in.read(col, channels);
						// Read in number of channels worth of bytes per pixel
						// File format is little-endian [A]BGR, or L
						imgData[i+0] = col[2];
						imgData[i+1] = col[1];
						imgData[i+2] = col[0];
						imgData[i+3] = col[3];

					} else {
						std::cout << "Error loading: " << filename << " image data parser error.\n";
						std::cout << "Read: " << i << " bytes of " << imgDataLen << " total bytes\n";
						std::cout << "eof: " << in.eof() << " bad: " << in.bad() << " fail: " << in.fail() << "\n";
						exit(1);
					}
				}
		} else {
			// Read in channels of data at a time and store
			char *col = new char[channels];
			for (unsigned int i=0; i<imgDataLen; i+=channels) {
				if (in.good()) {
					in.read(col, channels);
					// Read in number of channels worth of bytes per pixel
					// File format is little-endian [A]BGR, or L
					for (int j=0; j<channels; j++)
						imgData[i+j] = col[channels-j-1];

				} else {
					std::cout << "Error loading: " << filename << " image data parser error.\n";
					std::cout << "Read: " << i << " bytes of " << imgDataLen << " total bytes\n";
					std::cout << "eof: " << in.eof() << " bad: " << in.bad() << " fail: " << in.fail() << "\n";
					exit(1);
				}
			}
		}
		delete col;

		in.close();
	}

};

#endif
